package com.ideaaedi.springcloud.jd.commonspring.enhancer;

import com.google.common.collect.Lists;
import com.ideaaedi.springcloud.jd.commonds.entity.PageInfo;
import com.ideaaedi.springcloud.jd.commonds.support.DictFiller;
import com.ideaaedi.springcloud.jd.commonds.support.FileUrlFiller;
import com.ideaaedi.springcloud.jd.commonds.support.Filler;
import com.ideaaedi.springcloud.jd.commonspring.annotation.FillReturn;
import com.ideaaedi.springcloud.jd.commonspring.support.CommonService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 返回值填充aop实现
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class FillReturnAdvice {
    
    public static final String BEAN_NAME = "fillReturnAdvice";
    
    @Resource
    private CommonService commonService;
    
    @Pointcut("("
            + "@within(com.ideaaedi.springcloud.jd.commonspring.annotation.FillReturn)"
            + " || "
            + "@annotation(com.ideaaedi.springcloud.jd.commonspring.annotation.FillReturn)"
            + ")"
            + " && "
            + "!@annotation(com.ideaaedi.springcloud.jd.commonspring.annotation.IgnoreFillReturn)")
    public void executeAdvice() {
    }
    
    
    @AfterReturning(value = "executeAdvice()", returning = "returnObj")
    public void afterReturningAdvice(JoinPoint joinPoint, Object returnObj) {
        if (returnObj == null) {
            return;
        }
        // 获取注解信息
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        FillReturn fillReturnAnno = method.getAnnotation(FillReturn.class);
        if (fillReturnAnno == null) {
            fillReturnAnno = method.getDeclaringClass().getAnnotation(FillReturn.class);
        }
        Objects.requireNonNull(fillReturnAnno, "fillReturnAnno should not be null.");
        FillReturn.Item[] items = fillReturnAnno.items();
        if (items.length == 0) {
            return;
        }
        // 获取要填充的数据
        Map<FillReturn.Item, List<Filler>> itemClassifyMap = classify(returnObj);
        for (FillReturn.Item item : items) {
            List<Filler> fillers = itemClassifyMap.get(item);
            if (CollectionUtils.isEmpty(fillers)) {
                continue;
            }
            switch (item) {
                case DICT:
                    commonService.fillDictUrl(fillers.stream().map(x -> (DictFiller)x).collect(Collectors.toList()));
                    break;
                case FILE_URL:
                    commonService.fillFileUrl(fillers.stream().map(x -> (FileUrlFiller)x).collect(Collectors.toList()));
                    break;
                default:
                    throw new UnsupportedOperationException("un-support FillReturn fill item '" + item + "'");
            }
        }
    }
    
    /**
     * 解析returnObj，并将相关数据按照填充类型分类
     * <br />
     * 注：同一个对象可能出现在多个分类里
     *
     * @param returnObj 待解析对象
     *
     * @return  key-数据填充类型; value-待解析对象
     */
    public Map<FillReturn.Item, List<Filler>> classify(@NonNull Object returnObj) {
        Map<FillReturn.Item, List<Filler>> map = new HashMap<>();
        map.put(FillReturn.Item.DICT, Lists.newArrayList());
        map.put(FillReturn.Item.FILE_URL, Lists.newArrayList());
        // 普通对象
        if (returnObj instanceof DictFiller) {
            map.get(FillReturn.Item.DICT).add((DictFiller) returnObj);
        }
        if (returnObj instanceof FileUrlFiller) {
            map.get(FillReturn.Item.FILE_URL).add((FileUrlFiller) returnObj);
        }
        // 分页对象
        if (returnObj instanceof PageInfo) {
            List<?> dataList =
                    ((PageInfo<?>) returnObj).getDataList().stream().filter(Objects::nonNull).collect(Collectors.toList());
            parseList(map, dataList);
        }
        // 集合对象
        if (returnObj instanceof Collection<?>) {
            List<?> dataList = ((Collection<?>) returnObj).stream().filter(Objects::nonNull).collect(Collectors.toList());
            parseList(map, dataList);
        }
        return map;
    }
    
    @SuppressWarnings("unchecked")
    private void parseList(@NonNull Map<FillReturn.Item, List<Filler>> map, @NonNull List<?> dataList) {
        if (!CollectionUtils.isEmpty(dataList)) {
            Class<?> firstItemClazz = dataList.get(0).getClass();
            if (DictFiller.class.isAssignableFrom(firstItemClazz)) {
                map.get(FillReturn.Item.DICT).addAll((List<DictFiller>) dataList);
            }
            if (FileUrlFiller.class.isAssignableFrom(firstItemClazz)) {
                map.get(FillReturn.Item.FILE_URL).addAll((List<FileUrlFiller>) dataList);
            }
        }
    }
}
